package it.koutifaris.travianmap.gui;

import it.koutifaris.travianmap.db.impl.WorldMapDaoRandomImpl;
import it.koutifaris.travianmap.model.MapSquare;
import it.koutifaris.travianmap.model.Village;
import it.koutifaris.travianmap.transformation.Point2SquareTransformation;
import it.koutifaris.travianmap.worldmap.WorldMap;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

public class MapView extends View {

	private static final Paint BORDER;
	private static final Paint BORDER_BOLD;
	private static final Paint DESCRIPTION_TEXT;
	private static final Paint RED;
	private static final Paint BG_4_TEXT;
	private static final Paint COORDS;
	private static final int DEFAULT_TXT_SIZE = 14;

	static {
		BORDER = new Paint();
		BORDER.setColor(Color.GRAY);
		BORDER.setStyle(Style.STROKE);

		BORDER_BOLD = new Paint();
		BORDER_BOLD.setColor(Color.BLUE);
		BORDER_BOLD.setStyle(Style.STROKE);

		COORDS = new Paint();
		COORDS.setColor(Color.BLACK);
		COORDS.setStyle(Style.STROKE);
		COORDS.setTextSize(DEFAULT_TXT_SIZE);

		DESCRIPTION_TEXT = new Paint();
		DESCRIPTION_TEXT.setColor(Color.BLACK);
		DESCRIPTION_TEXT.setTextSize(DEFAULT_TXT_SIZE);
		BG_4_TEXT = new Paint();
		BG_4_TEXT.setColor(Color.WHITE);
		BG_4_TEXT.setStyle(Style.FILL);
		BG_4_TEXT.setAlpha(200);

		RED = new Paint();
		RED.setColor(Color.RED);
		RED.setStyle(Style.STROKE);
	}
	private WorldMap worldMap;
	private int dX = 0;
	private int dY = 0;
	private float zoom = 1f;
	private MapImageFactory mapImageFactory;
	private GestureDetector gd;
	private ScaleGestureDetector sd;

	private Point2SquareTransformation scrollPhysics;

	public MapView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}

	public MapView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	public MapView(Context context) {
		super(context);
		init(context);
	}

	private void init(Context context) {
		this.setBackgroundColor(Color.rgb(198, 239, 173));
		this.mapImageFactory = new MapImageFactory(getSqDim());

		MapViewGestureListener gestureListener = new MapViewGestureListener(this);
		gd = new GestureDetector(context, gestureListener);
		sd = new ScaleGestureDetector(context, gestureListener);
		this.setOnTouchListener(new View.OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				sd.onTouchEvent(event);
				gd.onTouchEvent(event);
				return true;
			}
		});
	}

	public WorldMap getWorldMap() {
		if (worldMap == null) {
			worldMap = new WorldMap(new WorldMapDaoRandomImpl());
		}
		return worldMap;
	}

	public void setWorldMap(WorldMap worldMap) {
		this.worldMap = worldMap;
	}

	public int getdX() {
		return dX;
	}

	public void setdX(int dX) {
		this.dX = dX;
	}

	public int getdY() {
		return dY;
	}

	protected MapSquare mapSquareAt(int touchX, int touchY) {
		// int squareX = getScrollPhysics().getPointToSquare().getX(touchX);
		// int squareY = getScrollPhysics().getPointToSquare().getY(touchY);
		//
		// return getWorldMap().getSquare(squareX, squareY);

		return null;
	}

	public void setSqCoords(int x, int y) {
		this.setdX(-x * getSqDim());
		this.setdY(y * getSqDim());
	}

	public void animateTo(int x, int y) {
		x = -x * getSqDim();
		y = y * getSqDim();
		int x0 = getdX();
		int y0 = getdY();
		final float V = ((float) (getWorldMap().getBounds().bottom - getWorldMap().getBounds().top) * getSqDim()) / 3f;
		// Log.i(this.getClass().getName(),"V: "+ V);
		double arc = Math.atan2(x - x0, y - y0);
		float vx = (float) (V * Math.sin(arc));
		float vy = (float) (V * Math.cos(arc));
		animationStep(vx, vy, x0, y0, x, y, System.currentTimeMillis());
	}

	private void animationStep(
			final float vx,
			final float vy,
			final int x0,
			final int y0,
			final int xf,
			final int yf,
			final long t0) {

		long tm = System.currentTimeMillis() - t0;
		float t = tm / 1000f;
		int x = (int) (vx * t + x0);
		int y = (int) (vy * t + y0);
		this.setdX(x);
		this.setdY(y);
		this.invalidate();

		boolean nextStep = Math.signum(xf - x) + Math.signum(vx) != 0 && Math.signum(yf - y) + Math.signum(vy) != 0;
		if (nextStep) {
			post(new Runnable() {
				@Override
				public void run() {
					animationStep(vx, vy, x0, y0, xf, yf, t0);
				}
			});
		} else {
			this.setdX(xf);
			this.setdY(yf);
		}
	}

	public void setdY(int dY) {
		this.dY = dY;
	}

	public int getSqDim() {
		return (int) (58 * getZoom());
	}

	public float getZoom() {
		return zoom;
	}

	public void setZoom(float zoom) {
		this.zoom = zoom;
		int txtSize = Math.max(10, (int) (DEFAULT_TXT_SIZE * getZoom()));
		this.mapImageFactory = new MapImageFactory(getSqDim());
		DESCRIPTION_TEXT.setTextSize(txtSize);
		COORDS.setTextSize(txtSize);
	}

	// @Override
	// protected void onLayout(boolean changed, int left, int top, int right,int
	// bottom) {
	// super.onLayout(changed, left, top, right, bottom);
	// dX = (right-left)/2;
	// dY = (bottom-top)/2;
	// }

	@Override
	protected void onDraw(Canvas canvas) {
		drawVillages(canvas);
		drawBorders(canvas);
		drawCoords(canvas);
	}

	private void drawCoords(Canvas c) {
		int sqDim = getSqDim();
		int sqW = 1 + c.getWidth() / sqDim;
		int sqH = 1 + c.getHeight() / sqDim;
		int sqx0 = -getdX() / sqDim;
		int sqy0 = -getdY() / sqDim;
		float textH = COORDS.getTextSize();

		int maxTxtW = 0;
		for (int i = -1; i < sqH + 1; i++) {
			int y = toroidY(-(sqy0 + i));
			int w = (int) COORDS.measureText(String.valueOf(y));
			if (w > maxTxtW)
				maxTxtW = w;
		}

		int txtH = (int) COORDS.getTextSize();
		c.drawRect(0, 0, c.getWidth(), txtH, BG_4_TEXT);
		c.drawRect(0, txtH, maxTxtW, c.getHeight(), BG_4_TEXT);

		// Columns
		for (int i = -1; i < sqW + 1; i++) {
			String txt = String.valueOf(toroidX(sqx0 + i));
			int w = (int) COORDS.measureText(txt);
			int dx = (sqDim - w) / 2;
			c.drawText(txt, i * sqDim + getdX() % sqDim + dx, textH, COORDS);
		}
		// Lines
		int dy = (sqDim - txtH) / 2;
		for (int i = -1; i < sqH + 1; i++) {
			int y = toroidY(-(sqy0 + i));
			c.drawText(String.valueOf(y), 0, i * getSqDim() + getdY() % sqDim + textH + dy, COORDS);
		}
	}

	private int toroidX(int sqx) {
		return 0;
	}

	private int toroidY(int sqy) {
		return 0;
	}

	private void drawBorders(Canvas c) {
		int cW = c.getWidth();
		int cH = c.getHeight();
		int sqDim = getSqDim();
		int sqW = 1 + c.getWidth() / sqDim;
		int sqH = 1 + c.getHeight() / sqDim;
		int sqx0 = -getdX() / sqDim;
		int sqy0 = -getdY() / sqDim;

		// Columns
		for (int i = -1; i < sqW + 1; i++) {
			Paint p = BORDER;
			int x = toroidX(sqx0 + i);
			if (x == getWorldMap().getBounds().left)
				p = BORDER_BOLD;
			c.drawLine(i * sqDim + getdX() % sqDim, 0, i * sqDim + getdX() % sqDim, cH, p);
		}
		// Lines
		for (int i = -1; i < sqH + 1; i++) {
			Paint p = BORDER;
			int y = toroidY(-(sqy0 + i));
			if (y == getWorldMap().getBounds().bottom)
				p = BORDER_BOLD;
			c.drawLine(0, i * getSqDim() + getdY() % sqDim, cW, i * getSqDim() + getdY() % sqDim, p);
		}
	}

	private void drawVillages(Canvas c) {
		int sqDim = getSqDim();
		int sqW = 1 + c.getWidth() / sqDim;
		int sqH = 1 + c.getHeight() / sqDim;
		int sqx0 = -getdX() / sqDim;
		int sqy0 = getdY() / sqDim;
		for (int i = -1; i < sqW + 1; i++) {
			for (int j = -1; j < sqH + 1; j++) {
				int l = getdX() % sqDim + i * sqDim;
				int t = (getdY() % sqDim + (j) * sqDim);
				int sqx = toroidX(sqx0 + i);
				int sqy = toroidY(sqy0 - j);
				drawVillage(c, getWorldMap().getSquare(sqx, sqy), l, t, sqDim);
			}
		}
	}

	private void drawVillage(Canvas c, MapSquare square, int l, int t, int sqDim) {
		if (square == null)
			return;
		Village village = square.getVillage();
		if (square.getVillage() != null) {
			c.drawBitmap(mapImageFactory.getVillageBmp(getResources(), village.getOwner().getTribe()), l, t, null);
			drawLabel(c, getDescription(square), l, t);
			int r = l + sqDim;
			int b = t + sqDim;
			c.drawRect(l, t, r, b, BORDER);
		}
	}

	private void drawLabel(Canvas c, String description, int l, int t) {
		float txtH = DESCRIPTION_TEXT.getTextSize();
		float[] widths = new float[description.length()];
		DESCRIPTION_TEXT.getTextWidths(description, widths);
		float txtW = 0;
		int maxW = getSqDim();
		int count = 0;
		for (float w : widths) {
			if (txtW + w < maxW) {
				txtW += w;
				count++;
			} else
				break;
		}
		// float txtW = DESCRIPTION_TEXT.measureText(description);
		c.drawRect(l + 1, t, l + 1 + txtW, t + txtH + 1, BG_4_TEXT);
		c.drawText(description.substring(0, count), l, t + txtH, DESCRIPTION_TEXT);
	}

	private String getDescription(MapSquare square) {
		return square.getVillage().getName().substring(0, Math.min(15, square.getVillage().getName().length()));
	}

	public Point2SquareTransformation getScrollPhysics() {
		if (scrollPhysics == null) {
			scrollPhysics = new Point2SquareTransformation(58);
		}

		return scrollPhysics;
	}

}
